#include <iostream>
#include <vector>
#include <memory>
#include <cmath>

#include <cstdio>

using namespace std;
using TensorType = vector<float>;

#define CHECK(x, ret)                                                              \
    if (!(x)) {                                                                    \
        fprintf(stderr, "Error at %s:%d condition(%s)\n", __FILE__, __LINE__, #x); \
        return ret;                                                                 \
    }

namespace {
shared_ptr<TensorType> operator*(const shared_ptr<TensorType>& x, float a) {
    auto out = make_shared<TensorType>();
    for (auto e: *x) {
        out->push_back(e * a);
    }

    return out;
}

shared_ptr<TensorType> operator*(const shared_ptr<TensorType>& x1, const shared_ptr<TensorType>& x2) {
    CHECK(x1->size() == x2->size(), nullptr);

    auto out = make_shared<TensorType>();
    for (size_t i = 0; i < x1->size(); ++i) {
        out->push_back((*x1)[i] * (*x2)[i]);
    }

    return out;
}

shared_ptr<TensorType> exp(const shared_ptr<TensorType>& x) {
    auto out = make_shared<TensorType>();
    for (auto e: *x) {
        out->push_back(::exp(e));
    }

    return out;
}
}

class Variable{
public:
    Variable(const shared_ptr<TensorType>& data) {
        this->data = data;
    }

    shared_ptr<TensorType> data;
};

class Function {
public:
    Variable operator()(const Variable& input) {
        auto x = input.data;
        auto y = this->forward(x);

        return Variable(y);
    }
protected:
    virtual shared_ptr<TensorType> forward(const shared_ptr<TensorType>& in) = 0;

};

class Square : public Function {
protected:
    shared_ptr<TensorType> forward(const shared_ptr<TensorType>& in) override {
        return in * in;
    }
};

class Exp : public Function {
protected:
    shared_ptr<TensorType> forward(const shared_ptr<TensorType>& in) override {
        auto out = make_shared<TensorType>();

        return exp(in);
    }
};

int main() {
    Square A;
    Exp B;
    Square C;

    auto data = make_shared<TensorType>(TensorType{0.5});
    Variable x(data);

    auto a = A(x);
    auto b = B(a);
    auto c = C(b);

    cout << (*c.data)[0] << endl;

    return 0;
}